// 控制并发数
{
  // 根据并发数量分割数组（for + slice）实现
  const group = (list = [], max = 0) => {
    if (!list.length) {
      return list;
    }

    let result = [];
    for (let i = 0; i < list.length; i += max) {
      result.push(list.slice(i, i + max));
    }
    return result;
  };
  console.log(group([1, 2, 3, 4, 5], 3));

  // async + await 封装请求集合
  // 利用 Promise.allSettled，用法和 Promise.all 差不多，
  // 不同的是 Promise.allSettled 等所有任务结束之后才会返回结果，Promise.all 只要有一个reject就会返回结果
  const requestHandler = async (groupedUrl = [], callback = () => {}) => {
    if (!groupedUrl.length) {
      callback();
      return groupedUrl;
    }

    const data = await Promise.allSettled(groupedUrl.map((fn) => fn())).then(
      (res) => {
        res.map(callback);
      }
    );

    return data;
  };

  // 发送请求函数封装
  const sendRequest = async (urls = [], max = 0, callback = () => {}) => {
    if (!urls.length) {
      return urls;
    }

    // 分割请求数组
    const currUrls = group(urls, max);

    const results = [];
    console.log('start!');
    for (const currUrl of currUrls) {
      try {
        let result = await requestHandler(currUrl, callback);
        results.push(result);
      } catch (error) {
        console.log(error);
      }
    }
    console.log('end!');
    return results;
  };

  const p1 = () =>
    new Promise((resolve, reject) => setTimeout(reject, 1000, 'p1'));
  const p2 = () => Promise.resolve(2);
  const p3 = () =>
    new Promise((resolve, reject) => setTimeout(resolve, 2000, 'p3'));
  const p4 = () => Promise.resolve(4);
  const p5 = () =>
    new Promise((resolve, reject) => setTimeout(reject, 2000, 'p5'));
  const p6 = () => Promise.resolve(6);
  const p7 = () =>
    new Promise((resolve, reject) => setTimeout(resolve, 1000, 'p7'));
  const p8 = () => Promise.resolve(8);
  const p9 = () =>
    new Promise((resolve, reject) => setTimeout(reject, 1000, 'p9'));
  const p10 = () => Promise.resolve(10);
  const p11 = () =>
    new Promise((resolve, reject) => setTimeout(resolve, 2000, 'p11'));
  const p12 = () => Promise.resolve(12);
  const p13 = () =>
    new Promise((resolve, reject) => setTimeout(reject, 1000, 'p13'));
  const p14 = () => Promise.resolve(14);

  const ps = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14];

  sendRequest(ps, 3, ({ reason, value }) => {
    console.log(reason || value);
  });
}

{
  // 主要是利用了 Promise.race , 以及包装原来的 promise 实例进一个新的promise 实例，在 then 里面处理队列，即是把完成的自己移除队列。

  // iteratorFn 是一个返回 Promise 的函数
  async function eachLimit(limit, arr, iteratorFn) {
    const res = []; // 最终结果
    const activeList = []; // 当前正在请求的队列
    for (const item of arr) {
      const p = iteratorFn(item);
      res.push(p);
      // 队列控制（更新队列）
      // 检查当前请求是否结束，结束则踢出队列
      // （如何判断状态是已经完成呢，并且找到这个完成的 Promise 对象，把它踢出队列，以便于新的加入？答案是 Promise.race）
      const e = p.then(() => {
        // e就是 用 iteratorFn 包裹 Promise 的实例（pendding状态的）
        activeList.splice(activeList.indexOf(e), 1);
      });
      // 将每一个请求放入队列
      activeList.push(e);
      while (activeList.length >= limit) {
        // 进行请求限制
        // const fastResult = await Promise.race(activeList);
        // 那么利用 fastResult 怎么判断哪个 Promise 实例完成了?
        // 只能通过 Promise 完成状态进行处理
        await Promise.race(activeList);
      }
    }
    return Promise.all(res);
  }

  // 使用了 N 个序列，每个序列都是 promise.then 的链式调用。
  // 假设目前是 A, B, C，D, E 5个请求要执行。其中要求 3个并发执行。
  // 我们可以改造成 [A then D], [B then E] [C]

  async function asyncLoop(limit, arr, iteratorFn) {
    const queues = new Array(limit).fill(0).map(() => Promise.resolve());
    let index = 0;
    let add = (cb) => {
      index = (index + 1) % limit;
      return queues[index].then(() => cb());
    };

    const result = [];
    for (const item of arr) {
      result.push(add(() => iteratorFn(item)));
    }

    return await Promise.all(result);
  }

  async function test() {
    const timeout = (i) =>
      new Promise((resolve) => setTimeout(() => resolve(i), i));
    const results = await eachLimit(2, [1000, 5000, 3000, 2000], timeout);
    console.log(results);
  }
}
